#ifndef ZLOG_H_
#define ZLOG_H_

#include "../Common/GlobalDefines.h"
#include <vector>
#include <map>
#include <fstream>
#include <string>
#include <sstream>


GLOBAL_NAMESPACE_BEGIN

enum LogLevel
{
    LOGLEVEL_NORMAL = 0,
	LOGLEVEL_DEBUG,
	LOGLEVEL_INFO,
	LOGLEVEL_WARN,
	LOGLEVEL_ERROR,
    LOGLEVEL_CRITICAL
};

class LogListener
{
public:
	virtual ~LogListener(){}
	virtual void messageLogged( const std::string& message, 
                                LogLevel level, 
                                bool maskDebug,
                                const std::string& logName) = 0;
};

class Log
{
protected:
    std::ofstream   log_;
    LogLevel        logLevel_;
    bool            bDebugOut_;
    std::string     logName_;

    typedef std::vector<LogListener*> Listeners;

    Listeners listeners_;

public:
    class Stream;

    Log(const std::string &name, const std::string& fileName, bool debugOutput = true);
    ~Log();

    const std::string& getName() const { return logName_; }
    bool isDebugOutputEnabled() const { return bDebugOut_; }
    
    void logMessage(const std::string& message, LogLevel level = LOGLEVEL_NORMAL, bool maskDebug = false);
    Stream stream(LogLevel level = LOGLEVEL_NORMAL, bool maskDebug = false);

    void setDebugOutputEnabled(bool debugOutput);
    LogLevel getLogLevel() const { return logLevel_; }

    void addListener(LogListener* listener);
    void removeListener(LogListener* listener);

    class Stream
    {
    protected:
        Log        *target_;
        LogLevel    level_;
        bool        bMaskDebug_;
        std::stringstream ssCache_;

    public:
        struct Flush{};

        Stream(Log *target, LogLevel level, bool maskDebug)
            : target_(target), level_(level), bMaskDebug_(maskDebug)
        {}

        Stream(const Stream& rhs)
            : target_(rhs.target_), level_(rhs.level_), bMaskDebug_(rhs.bMaskDebug_)
        {
            ssCache_.str(rhs.ssCache_.str());
        }

        ~Stream()
        {
            if (ssCache_.tellp() > 0)
            {
                target_->logMessage(ssCache_.str(), level_, bMaskDebug_);
            }
        }

        template <typename T>
        Stream& operator<< (const T& v)
        {
            ssCache_ << v;
            return *this;
        }

        Stream& operator<<(const Flush& v)
        {
            (void)v;
            target_->logMessage(ssCache_.str(), level_, bMaskDebug_);
            ssCache_.str("");
            return *this;
        }
    };
};

GLOBAL_NAMESPACE_END

#endif//ZLOG_H_